package library.controller;

import library.ErrorCode;
import library.ErrorMessage;
import library.entity.Book;
import library.entity.BookStatus;
import library.entity.BookingRecord;
import library.entity.Reader;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;

public class BorrowController extends AbstractController<BookingRecord> {
	final public int loanDate = 14;
	final public int maxRenewCount = 5;
	final public int maxBorrowCount = 8;
	final public float lateReturnFine = 1.5f;

	protected static BookController bookController;
	protected Reader reader;
	protected List<BookingRecord> bookedRecord;
	protected List<BookingRecord> lateRecord;
	protected Calendar today;

	public BorrowController(Reader reader) throws SQLException {
		super(BookingRecord.class);
		bookController = new BookController();
		lateRecord = new ArrayList<BookingRecord>();
		today = Calendar.getInstance();
		this.reader = reader;
		bookedRecord = new ArrayList<BookingRecord>();
	}

	public List<BookingRecord> getBookingRecord() {
		try {
			bookedRecord = select("user_id", reader.getId());
		} catch (SQLException e) {
			e.printStackTrace();
			return null;
		}
		return this.bookedRecord;
	}

	public boolean renew(BookingRecord record) {
		if (record.getRenewCount() >= maxRenewCount) {
			lastError = new ErrorMessage(ErrorCode.RENEW_FAIL_MAX_COUNT_EXCESS, record);
			return false;
		} else {
			Calendar newReturnDate = calculateReturnDate();
			record.setReturnDate(newReturnDate);
			record.updateRenewCount();
			if(update(record)){
				getBookingRecord();
				return true;
			}
			return false;
		}
	}

	public boolean borrowBook(int bookid) {
		Book book = bookController.getBookById(bookid);
		if (book == null) {
			lastError = new ErrorMessage(ErrorCode.BOOK_NOT_FOUND);
			return false;
		}

		Calendar returnDate = calculateReturnDate();
		if (book.getStatus() != BookStatus.Available) {
			lastError = new ErrorMessage(ErrorCode.BOOK_ALREADY_RESERVED, book);
			return false;
		} else if (getExpiryRecord().size() > 0) {
			lastError = new ErrorMessage(ErrorCode.READER_HAVE_EXPIRE_BOOK, book);
			return false;
		} else if (getBookingRecord().size() >= 8){
			lastError = new ErrorMessage(ErrorCode.RENEW_FAIL_MAX_COUNT_EXCESS, book);
			return false;
		} else {
			BookingRecord booking = new BookingRecord(book, returnDate, reader);
			try {
				insert(booking);
				bookController.updateStatus(book, BookStatus.Reserved);
				return true;
			} catch (SQLException e) {
				lastError = new ErrorMessage(ErrorCode.UNEXPECTED_ERROR, book);
				return false;
			}
		}
	}

	public Calendar calculateReturnDate() {
		Calendar returnDate = Calendar.getInstance();
		returnDate.add(Calendar.DATE, loanDate);
		return returnDate;
	}

	public boolean isLateReturn(BookingRecord borrowRecord) {
		return borrowRecord.getReturnDate().before(today);
	}

	public boolean returnBook(Book book, boolean ignoreExpire) {
		BookingRecord booking = null;
		try {
			booking = selectOnce("res_id", book.getId());
			if (booking == null) {
				lastError = new ErrorMessage(ErrorCode.BOOK_NOT_RESERVED, book);
				return false;
			} else if (isLateReturn(booking) && !ignoreExpire) {
				lastError = new ErrorMessage(ErrorCode.BOOK_OVERDUE, booking);
				return false;
			}

			if (delete(booking)) {
				bookController.updateStatus(booking.getBook(),
						BookStatus.Available);
				return true;
			} else {
				return false;
			}
		} catch (Exception e) {
			lastError = new ErrorMessage(ErrorCode.UNEXPECTED_ERROR, e);
			return false;
		}
	}

	public boolean returnBook(Book book) {
		return returnBook(book, false);
	}

	public int calculateExpireDay(BookingRecord borrowRecord) {
		long diff = today.getTimeInMillis()
				- borrowRecord.getReturnDate().getTimeInMillis();
		int diffDay = (int) (diff / (60 * 60 * 1000 * 24));
		return diffDay;
	}

	public float calculateFine(BookingRecord borrowRecord){
		int expireDay = calculateExpireDay(borrowRecord);
		if(expireDay > 0)
			return expireDay * lateReturnFine;
		else
			return 0f;
	}
	
	public List<BookingRecord> getExpiryRecord() {
		getBookingRecord();
		for (BookingRecord b : bookedRecord) {
			if (b.getReturnDate().before(today))
				lateRecord.add(b);
		}
		return lateRecord;
	}
}
